<template>
  <div ref="canvas"></div>
</template>

<script lang="ts" setup>
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import { GUI } from "three/examples/jsm/libs/lil-gui.module.min.js";
import Stats from "three/examples/jsm/libs/stats.module";
import { ref, onMounted } from "vue";

// 画布
const canvas = ref<any>();
// 场景
const scene = new THREE.Scene();
// 相机
const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000)
camera.position.set(-80, 80, 80);
camera.lookAt(0, 0, 0);
// 渲染器
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setClearColor(0x000000);
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
// 性能统计
const stat = Stats();
// 相机控件
const orbitControl = new OrbitControls(camera, renderer.domElement);

// 平面
const planeGeometry = new THREE.PlaneGeometry(600, 200, 20, 20);
const planeMaterial = new THREE.MeshLambertMaterial({
  color: 0xffffff,
  side: THREE.DoubleSide,
});
const plane = new THREE.Mesh(planeGeometry, planeMaterial);
plane.receiveShadow = true;
plane.rotation.x = -0.5 * Math.PI;
plane.position.set(15, -5, 0);
// cube
const cubeGeometry = new THREE.BoxGeometry(4, 4, 4);
const cubeMaterial = new THREE.MeshLambertMaterial({
  color: 0xff3333,
});
const cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
cube.castShadow = true;
cube.position.set(-4, 3, 0);
// sphere
const sphereGeometry = new THREE.SphereGeometry(4, 20, 20);
const sphereMaterial = new THREE.MeshLambertMaterial({
  color: 0x7777ff,
});
const sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
sphere.castShadow = true;
sphere.position.set(20, 0, 2);
// add plane cube sphere to scene
scene.add(plane, cube, sphere);
// 环境光
const ambient = new THREE.AmbientLight("#1c1c1c");
scene.add(ambient);
// 平行光 - 所有的光线都是平行的，所以不会有光锥，而是一个立方体区域
const directional = new THREE.DirectionalLight("#ff5808");
directional.position.set(-40, 60, -10);
directional.castShadow = true;
directional.shadow.camera.near = 2;
directional.shadow.camera.far = 80;
directional.shadow.camera.left = -30;
directional.shadow.camera.right = 30;
directional.shadow.camera.top = 30;
directional.shadow.camera.bottom = -30;
directional.intensity = 0.5;
directional.shadow.mapSize.width = 1024;
directional.shadow.mapSize.height = 1024;
scene.add(directional);
// 阴影相机辅助坐标系
const shadowCameraHelper = new THREE.CameraHelper(directional.shadow.camera);
// 平行光
const sphereLightGeometry = new THREE.SphereGeometry(0.2);
const sphereLightMaterial = new THREE.MeshBasicMaterial({
  color: 0xac6c25
});
const sphereLightMesh = new THREE.Mesh(sphereLightGeometry, sphereLightMaterial);
sphereLightMesh.castShadow = true;
sphereLightMesh.position.set(3, 20, 3);
scene.add(sphereLightMesh);

let step = 0;
// gui
const controlObj = {
  rotationSpeed : 0.03,
  bouncingSpeed : 0.03,
  ambientColor : ambient.color.getStyle(),
  directionalColor : directional.color.getStyle(),
  intensity : 0.5,
  debug : false,
  castShadow : true,
  onlyShadow : false,
  target : "Plane",
};
const gui = new GUI();
gui.addColor(controlObj, 'ambientColor').name("环境光颜色").onChange((e) => {
  ambient.color = new THREE.Color(e);
});
gui.addColor(controlObj, 'directionalColor').name("平行光颜色").onChange((e) => {
  directional.color = new THREE.Color(e);
});
// @ts-ignore
gui.add(controlObj, 'intensity', 0, 5).name("平行光强度").step(0.1).onChange((e) => {
  directional.intensity = e;
});
// @ts-ignore
gui.add(controlObj, 'debug').name("平行光调试模式").onChange((e) => {
  if (e) {
    scene.add(shadowCameraHelper);
  } else {
    scene.remove(shadowCameraHelper);
  }
});
// @ts-ignore
gui.add(controlObj, 'castShadow').name("启用平行光阴影").onChange((e) => {
  directional.castShadow = e;
});
// @ts-ignore
gui.add(controlObj, 'onlyShadow').name("onlyShadow").onChange((e) => {
  // @ts-ignore
  directional.onlyShadow = e;
});
// @ts-ignore
gui.add(controlObj, 'target', ['Plane', 'Sphere', 'Cube']).name("平行光照射目标").onChange((e) => {
  switch (e) {
    case "Plane":
      directional.target = plane;
      break;
    case "Sphere":
      directional.target = sphere;
      break;
    case "Cube":
      directional.target = cube;
      break;
  }
});
// 渲染函数
const render = () => {
  stat.update();
  orbitControl.update();

  // rotate the cube around its axes
  cube.rotation.x += controlObj.rotationSpeed;
  cube.rotation.y += controlObj.rotationSpeed;
  cube.rotation.z += controlObj.rotationSpeed;

  // bounce the sphere up and down
  step += controlObj.bouncingSpeed;
  sphere.position.x = 20 + (10 * (Math.cos(step)));
  sphere.position.y = 2 + (10 * Math.abs(Math.sin(step)));

  sphereLightMesh.position.z = -8;
  sphereLightMesh.position.y = +(27 * (Math.sin(step / 3)));
  sphereLightMesh.position.x = 10 + (26 * (Math.cos(step / 3)));

  directional.position.copy(sphereLightMesh.position);

  requestAnimationFrame(render);
  renderer.render(scene, camera);
};

onMounted(() => {
  canvas.value.appendChild(stat.domElement);
  canvas.value.appendChild(renderer.domElement);
  render();
});

</script>
